/* Orbas:
 *     A open source CORBA Specification implementation from Huihoo.
 *
 * Copyright 2002-2003 Huihoo.org, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */



package org.huihoo.orbas.orb.giop;

/**
 * <p>Description: </p>
 * Server side GIOP implementation.
 * <p>Copyright (c) 2002,2003</p>
 * <p>Company: <a href="http://www.huihoo.org/">huihoo.org</a></p>
 * @author <a href="http://www.huihoo.org/~mep">mep(mep@huihoo.com)</a>
 * @see <a href="http://www.huihoo.org/orbas">http://www.huihoo.org/orbas</a>
 * @version 1.0
 */


import com.cognition.util.j2me.Logger;
import com.cognition.util.j2me.SystemLogger;
import java.io.IOException;
import java.util.Vector;
import javax.microedition.io.file.ConnectionClosedException;
import org.huihoo.orbas.orb.cdr.*;
//import org.omg.GIOP.*;
import org.huihoo.orbas.orb.protocols.Transport;
import org.huihoo.orbas.orb.protocols.Channel;
//import org.omg.IOP.*;
import org.omg.IOP.ServiceContext;
import org.huihoo.orbas.orb.ORB;

public class GiopServerWorker extends GiopWorker
                        implements org.huihoo.orbas.orb.ServerWorker,
                        org.omg.CORBA.portable.ResponseHandler {
    private static Logger logger = SystemLogger.getLogger(SystemLogger.LOGGER_CONSOLE);
    public static final String LOG_TAG = "GiopServerWorker";

    protected Transport transport;
    
    private java.io.InputStream in;
    private ORB orb;
    
    int requestId;

    int version;


    public GiopServerWorker(ORB orb,int version) {
        this.orb = orb;
        this.version = version;
    }
    
    public GiopServerWorker(ORB orb,Transport transport,int version) {
        this.orb = orb;
        this.transport = transport;
        if( transport == null ) {
            System.out.println("GiopServerWorker:got null transport");
        }
        this.in = transport.getInputStream();
        this.version = version;
    }


    private int readConnection(byte[] buffer) throws IOException, ConnectionClosedException{
        int bytesRead = 0;

        if (buffer == null || buffer.length == 0)
            throw new IOException("null or zero length buffer");

        // perform a blocking read
        if ((bytesRead = in.read(buffer, 0, buffer.length)) == -1)
            throw new ConnectionClosedException("Connection closed by peer");

        if (bytesRead != buffer.length)
            throw new IOException("Protocol violation: Wrong GIOP Header length of " + bytesRead);

        if (buffer[0] != 0x47 || buffer[1] != 0x49 || buffer[2] != 0x4f || buffer[3] != 0x50)
            throw new IOException("Protocol violation: Malformed GIOP Header");
            // TODO: send back GIOP Error diagram

        return bytesRead;


    }

    private void processConnection_1_0(byte[] buffer, int bytesRead) throws IOException{
        if (buffer == null || buffer.length == 0)
            throw new IOException("buffer must not be null or empty.");

        byte reponseExpected;

        CDRInputStream headerInput = new CDRInputStream(orb, buffer);

        headerInput.mark();
        org.omg.GIOP.MessageHeader_1_0 header0 =
                org.omg.GIOP.MessageHeader_1_0Helper.read(headerInput);

        boolean isLittleEndian = header0.byte_order;

        headerInput.setLittleEndian(isLittleEndian);

        headerInput.reset();
        header0 = org.omg.GIOP.MessageHeader_1_0Helper.read(headerInput);

        int messageSize = header0.message_size;

        byte[] mesg = new byte[messageSize];

        try {
            if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
                System.out.println("Connection closed by peer");
                return;
            }
        } catch (java.io.IOException e) {
            System.out.println("Communication Error:" + e.toString());
            return;
        }

        if (bytesRead != messageSize) {
            System.out.println("GIOP message not of correct length");
            return;
        }

        CDRInputStream msgIn = new CDRInputStream(orb, mesg);
        msgIn.setLittleEndian(isLittleEndian);

        this.version = 10;
        org.omg.GIOP.TargetAddress target;
        
        int type = (int) header0.message_type;
        switch (type) {
            case org.omg.GIOP.MsgType_1_0._Request:
                org.omg.GIOP.RequestHeader_1_0 request =
                        org.omg.GIOP.RequestHeader_1_0Helper.read(msgIn);

                target = new org.omg.GIOP.TargetAddress();
                target.object_key(request.object_key);

                if (request.response_expected == true) {
                    reponseExpected = 1;
                } else {
                    reponseExpected = 0;
                }

                handleRequest(request.request_id,
                        request.operation,
                        request.service_context,
                        target,
                        reponseExpected,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_0._Reply:
                org.omg.GIOP.ReplyHeader_1_0 reply =
                        org.omg.GIOP.ReplyHeader_1_0Helper.read(msgIn);

                handleReply(reply.request_id,
                        reply.reply_status.value(),
                        reply.service_context,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_0._LocateRequest:
                org.omg.GIOP.LocateRequestHeader_1_0 locateRequest =
                        org.omg.GIOP.LocateRequestHeader_1_0Helper.read(msgIn);

                target = new org.omg.GIOP.TargetAddress();
                target.object_key(locateRequest.object_key);
                handleLocateRequest(locateRequest.request_id,
                        target,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_0._LocateReply:
                org.omg.GIOP.LocateReplyHeader_1_0 locateReply =
                        org.omg.GIOP.LocateReplyHeader_1_0Helper.read(msgIn);

                handleLocateReply(locateReply.request_id,
                        locateReply.locate_status.value(),
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_0._CancelRequest:
                org.omg.GIOP.CancelRequestHeader cancelRequest =
                        org.omg.GIOP.CancelRequestHeaderHelper.read(msgIn);

                handleCancelRequest(cancelRequest.request_id);

            case org.omg.GIOP.MsgType_1_0._CloseConnection:

                handleCloseConnection();
                break;

            case org.omg.GIOP.MsgType_1_0._MessageError:

                handleMessageError();
                break;

            default:
            //error
        }


    }

    private void processConnection_1_1(byte[] buffer, int bytesRead)
            throws IOException{
        if (buffer == null || buffer.length == 0)
            throw new IOException("buffer must not be null or empty.");

        byte reponseExpected;
        CDRInputStream headerInput = new CDRInputStream(orb, buffer);

        headerInput.mark();
        org.omg.GIOP.MessageHeader_1_1 header1 =
                org.omg.GIOP.MessageHeader_1_1Helper.read(headerInput);

        boolean isLittleEndian = ((header1.flags & 0x01) == 0x1) ? true:false;
        if ((header1.flags & 0x01) == 0x1) {
            isLittleEndian = true;
        } else {
            isLittleEndian = false;
        }

        headerInput.setLittleEndian(isLittleEndian);

        headerInput.reset();
        header1 = org.omg.GIOP.MessageHeader_1_1Helper.read(headerInput);
        int messageSize = header1.message_size;

        byte[] mesg = new byte[messageSize];

        try {
            if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
                System.out.println("Connection closed by peer");
                return;
            }
        } catch (java.io.IOException e) {
            System.out.println("Communication Error:" + e.toString());
            e.printStackTrace();
            return;
        }

        if (bytesRead != messageSize) {
            System.out.println("GIOP msg not of correct length");
            return;
        }

        CDRInputStream msgIn = new CDRInputStream(orb, mesg);
        msgIn.setLittleEndian(isLittleEndian);

        this.version = 11;

        int type = (int) header1.message_type;
        switch (type) {
            case org.omg.GIOP.MsgType_1_1._Request:
                org.omg.GIOP.RequestHeader_1_1 request =
                        org.omg.GIOP.RequestHeader_1_1Helper.read(msgIn);

                org.omg.GIOP.TargetAddress target = new org.omg.GIOP.TargetAddress();
                target.object_key(request.object_key);

                if (request.response_expected == true) {
                    reponseExpected = 1;
                } else {
                    reponseExpected = 0;
                }

                handleRequest(request.request_id,
                        request.operation,
                        request.service_context,
                        target,
                        reponseExpected,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._Reply:
                org.omg.GIOP.ReplyHeader_1_0 reply =
                        org.omg.GIOP.ReplyHeader_1_1Helper.read(msgIn);

                handleReply(reply.request_id,
                        reply.reply_status.value(),
                        reply.service_context,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._LocateRequest:
                org.omg.GIOP.LocateRequestHeader_1_0 locateRequest =
                        org.omg.GIOP.LocateRequestHeader_1_1Helper.read(msgIn);

                target = new org.omg.GIOP.TargetAddress();
                target.object_key(locateRequest.object_key);
                handleLocateRequest(locateRequest.request_id,
                        target,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._LocateReply:
                org.omg.GIOP.LocateReplyHeader_1_0 locateReply =
                        org.omg.GIOP.LocateReplyHeader_1_1Helper.read(msgIn);

                handleLocateReply(locateReply.request_id,
                        locateReply.locate_status.value(),
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._CancelRequest:
                org.omg.GIOP.CancelRequestHeader cancelRequest =
                        org.omg.GIOP.CancelRequestHeaderHelper.read(msgIn);

                handleCancelRequest(cancelRequest.request_id);

            case org.omg.GIOP.MsgType_1_1._CloseConnection:
                handleCloseConnection();
                break;

            case org.omg.GIOP.MsgType_1_1._MessageError:
                handleMessageError();
                break;

            case org.omg.GIOP.MsgType_1_1._Fragment:
                handleFragment();
                break;

            default:
            //error
        }


    }

    private void processConnection_1_2(byte[] buffer, int bytesRead)
            throws IOException{

        if (buffer == null || buffer.length == 0)
            throw new IOException("buffer must not be null or empty.");

        CDRInputStream headerInput = new CDRInputStream(orb, buffer);

        headerInput.mark();
        org.omg.GIOP.MessageHeader_1_1 header2 =
                org.omg.GIOP.MessageHeader_1_2Helper.read(headerInput);

        boolean isLittleEndian = ((header2.flags & 0x01) == 0x1) ? true:false;

        headerInput.setLittleEndian(isLittleEndian);

        headerInput.reset();
        header2 = org.omg.GIOP.MessageHeader_1_2Helper.read(headerInput);
        int messageSize = header2.message_size;

        byte[] mesg = new byte[messageSize];

        try {
            if ((bytesRead = in.read(mesg, 0, messageSize)) == -1) {
                System.out.println("Connection closed by peer");
                return;
            }
        } catch (java.io.IOException e) {
            System.out.println("Communication Error:" + e.toString());
            e.printStackTrace();
            return;
        }

        if (bytesRead != messageSize) {
            System.out.println("GIOP msg not of correct length");
            return;
        }

        CDRInputStream msgIn = new CDRInputStream(orb, mesg);
        msgIn.setLittleEndian(isLittleEndian);

        this.version = 12;

        int type = (int) header2.message_type;
        switch (type) {
            case org.omg.GIOP.MsgType_1_1._Request:
                org.omg.GIOP.RequestHeader_1_2 request =
                        org.omg.GIOP.RequestHeader_1_2Helper.read(msgIn);

                handleRequest(request.request_id,
                        request.operation,
                        request.service_context,
                        request.target,
                        request.response_flags,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._Reply:
                org.omg.GIOP.ReplyHeader_1_2 reply =
                        org.omg.GIOP.ReplyHeader_1_2Helper.read(msgIn);

                handleReply(reply.request_id,
                        reply.reply_status.value(),
                        reply.service_context,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._LocateRequest:
                org.omg.GIOP.LocateRequestHeader_1_2 locateRequest =
                        org.omg.GIOP.LocateRequestHeader_1_2Helper.read(msgIn);

                handleLocateRequest(locateRequest.request_id,
                        locateRequest.target,
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._LocateReply:
                org.omg.GIOP.LocateReplyHeader_1_2 locateReply =
                        org.omg.GIOP.LocateReplyHeader_1_2Helper.read(msgIn);

                handleLocateReply(locateReply.request_id,
                        locateReply.locate_status.value(),
                        msgIn);
                break;

            case org.omg.GIOP.MsgType_1_1._CancelRequest:
                org.omg.GIOP.CancelRequestHeader cancelRequest =
                        org.omg.GIOP.CancelRequestHeaderHelper.read(msgIn);

                handleCancelRequest(cancelRequest.request_id);

            case org.omg.GIOP.MsgType_1_1._CloseConnection:
                handleCloseConnection();
                break;

            case org.omg.GIOP.MsgType_1_1._MessageError:
                handleMessageError();
                break;

            case org.omg.GIOP.MsgType_1_1._Fragment:
                handleFragment();
                break;

            default:
            //error
        }


    }
    private void processConnection() throws IOException, ConnectionClosedException{
        int messageHeaderLength = 12;

        // read the next set of bytes and run a check.
        byte[] buffer = new byte[messageHeaderLength];
        int bytesRead = readConnection(buffer);

        CDRInputStream istream = new CDRInputStream(orb, buffer);

        istream.read_long();
        org.omg.GIOP.Version version = org.omg.GIOP.VersionHelper.read(istream);

        //pre-declaration of local variavles
        /*
        int messageSize;
        boolean isLittleEndian;
        byte reponseExpected;
        CDRInputStream headerInput;
        byte[] mesg;
        org.omg.GIOP.TargetAddress target;
        CDRInputStream msgIn = null;
        int type;
         */

        if (version.major == 1) {
            switch (version.minor) {
                case 0:
                    processConnection_1_0(buffer, bytesRead);
                    break;  //minor version is 0

                case 1:

                    processConnection_1_1(buffer, bytesRead);
                    break;   //minor version is 1

                case 2:
                    processConnection_1_2(buffer, bytesRead);
                    break;   //minor version is 2

                default:
            }

        }

    }
    public void run() {
        
        while(true) {
            try {
                processConnection();
            } catch (ConnectionClosedException cce){
                return;
            } catch (IOException ioe){
                return;
            } catch(Exception ex) {
                System.out.println("Exception: " + ex.toString());
                
                //ignore this,go to next message
                continue;
            }
        }
        
    }

    public void runNonContinuous(){
        try {
            processConnection();
        } catch (IOException ex) {
            logger.debug(LOG_TAG, "IOException");
            ex.printStackTrace();
        } catch (ConnectionClosedException ex) {
            logger.debug(LOG_TAG, "connection closed");
            ex.printStackTrace();
        }
    }


    public void handleRequest(int request_id,
                              String op,
                              ServiceContext[] context,
                              org.omg.GIOP.TargetAddress target,
                              byte response_flags,
                              CDRInputStream in) {
            logger.debug(LOG_TAG, "handleRequest(op: " + op);
                        //just for test
//                System.out.println("operation: " + op);
//                System.out.println("parameter: " + in.read_string());
            //end of test

            this.requestId = request_id;
            CDROutputStream bodyOut = null;
            try {
                
                switch(target.discriminator()) {
                    case org.omg.GIOP.KeyAddr.value:
                        bodyOut = 
                            (CDROutputStream)(orb.getDefaultIopEngine().invoke(target.object_key(), op, in, this));
                        
                        
                    case org.omg.GIOP.ProfileAddr.value:
                        //unpack profiles to objectkey
                        break;
                        
                    case org.omg.GIOP.ReferenceAddr.value:
                        //unpack ior to objectkey
                        break;
                        
                    default:
                        //error
                
                }
                
            }
            catch(org.omg.PortableServer.ForwardRequest forward) {
                //user request location forward
                //create a forward reply
                bodyOut = (CDROutputStream)createForwardReply(forward);
            }
            catch(org.omg.CORBA.SystemException se) {
                bodyOut = (CDROutputStream)createExceptionReply();
            }
            catch(org.omg.PortableServer.POAPackage.AdapterNonExistent ue) {
                bodyOut = (CDROutputStream)createExceptionReply();
                org.omg.PortableServer.POAPackage.AdapterNonExistentHelper.write(bodyOut, ue);
            }
            
            //create GIOP header
            CDROutputStream headerOut = new CDROutputStream(null);
            
            org.omg.GIOP.MessageHeader_1_0 messageHeader10 = 
                    new org.omg.GIOP.MessageHeader_1_0();
            
            messageHeader10.magic = new char[4];
            messageHeader10.magic[0] = 'G';
            messageHeader10.magic[1] = 'I';
            messageHeader10.magic[2] = 'O';
            messageHeader10.magic[3] = 'P';
            messageHeader10.GIOP_version = new org.omg.GIOP.Version();
            messageHeader10.GIOP_version.major = 1;
            messageHeader10.GIOP_version.minor = (byte)(this.version - 10);
            messageHeader10.byte_order = false;
            messageHeader10.message_type = org.omg.GIOP.MsgType_1_0._Reply;
            messageHeader10.message_size = bodyOut.getLength() + 1;
            
            org.omg.GIOP.MessageHeader_1_0Helper.write(headerOut,messageHeader10);
            
            headerOut.write_octet_array(bodyOut.getBuffer(), 0, bodyOut.getLength());
            //write out GIOP header
            
            try {
                //write reply
                write(headerOut);
                
            }
            catch (java.io.IOException e) {
                e.printStackTrace();
            }
            
           

    }
    
    public org.omg.CORBA.portable.OutputStream createReply() {
        
        CDROutputStream out = new CDROutputStream(orb);
        //create giop reply header(giop body)
        switch( version ) {
            case 10:
            case 11:
                //version 1.0 and 1.1 are the same in fact
                org.omg.GIOP.ReplyHeader_1_0 reply10 = new org.omg.GIOP.ReplyHeader_1_0();
                reply10.service_context = new org.omg.IOP.ServiceContext[0];
                reply10.request_id = requestId;
                reply10.reply_status = org.omg.GIOP.ReplyStatusType_1_0.NO_EXCEPTION;
                
                org.omg.GIOP.ReplyHeader_1_0Helper.write(out, reply10);
                break;
            case 12:
                org.omg.GIOP.ReplyHeader_1_2 reply12 = new org.omg.GIOP.ReplyHeader_1_2();
                reply12.service_context = new org.omg.IOP.ServiceContext[0];
                reply12.request_id = requestId;
                reply12.reply_status = org.omg.GIOP.ReplyStatusType_1_2.NO_EXCEPTION;
                
                org.omg.GIOP.ReplyHeader_1_2Helper.write(out, reply12);
                break;
            default:
                //error
        }
        
        return out;
    }
    
    public org.omg.CORBA.portable.OutputStream createExceptionReply() {
        CDROutputStream out = new CDROutputStream(orb);
        //create giop reply header(giop body)
        switch( version ) {
            case 10:
            case 11:
                org.omg.GIOP.ReplyHeader_1_0 reply10 = new org.omg.GIOP.ReplyHeader_1_0();
                reply10.service_context = new org.omg.IOP.ServiceContext[0];
                reply10.request_id = requestId;
                reply10.reply_status = org.omg.GIOP.ReplyStatusType_1_0.USER_EXCEPTION;
                
                org.omg.GIOP.ReplyHeader_1_0Helper.write(out, reply10);
                break;
            case 12:
                org.omg.GIOP.ReplyHeader_1_2 reply12 = new org.omg.GIOP.ReplyHeader_1_2();
                reply12.service_context = new org.omg.IOP.ServiceContext[0];
                reply12.request_id = requestId;
                reply12.reply_status = org.omg.GIOP.ReplyStatusType_1_2.USER_EXCEPTION;
                
                org.omg.GIOP.ReplyHeader_1_2Helper.write(out, reply12);
                break;
            default:
                //error
        }
        
        return out;
    }

    public org.omg.CORBA.portable.OutputStream createSystemExceptionReply() {
        CDROutputStream out = new CDROutputStream(orb);
        //create giop reply header(giop body)
        switch( version ) {
            case 10:
            case 11:
                org.omg.GIOP.ReplyHeader_1_0 reply10 = new org.omg.GIOP.ReplyHeader_1_0();
                reply10.service_context = new org.omg.IOP.ServiceContext[0];
                reply10.request_id = requestId;
                reply10.reply_status = org.omg.GIOP.ReplyStatusType_1_0.SYSTEM_EXCEPTION;
                
                org.omg.GIOP.ReplyHeader_1_0Helper.write(out, reply10);
                break;
            case 12:
                org.omg.GIOP.ReplyHeader_1_2 reply12 = new org.omg.GIOP.ReplyHeader_1_2();
                reply12.service_context = new org.omg.IOP.ServiceContext[0];
                reply12.request_id = requestId;
                reply12.reply_status = org.omg.GIOP.ReplyStatusType_1_2.SYSTEM_EXCEPTION;
                
                org.omg.GIOP.ReplyHeader_1_2Helper.write(out, reply12);
                break;
            default:
                //error
        }
        
        return out;
    }
    
    public org.omg.CORBA.portable.OutputStream createForwardReply(org.omg.PortableServer.ForwardRequest forward) {
        return null;
    }
    
    public void handleReply(int request_id,
                            int reply_status,
                            ServiceContext[] context,
                            CDRInputStream in) {
            
    }

    public void handleLocateRequest(int request_id,
                                    org.omg.GIOP.TargetAddress target,
                                    CDRInputStream in) {
            try {
                orb.getDefaultIopEngine().locate();
                
            }
            catch(org.omg.PortableServer.ForwardRequest forward) {
                //user request location forward
                //create a forward reply
            }
    }

    public void handleLocateReply(int request_id,
                                  int locate_status,
                                  CDRInputStream in) {
        
    }

    public void handleCancelRequest(int request_id) {
        throw new org.omg.CORBA.NO_IMPLEMENT();
    }

    public void handleCloseConnection() {
        throw new org.omg.CORBA.NO_IMPLEMENT();

    }

    public void handleMessageError() {
        System.out.println("GIOP Error Message received");

    }

    public void handleFragment() {
        throw new org.omg.CORBA.NO_IMPLEMENT();
    }

    public Transport getTransport() {
        return transport;
    }

 

}